package com.google.code.hibernate.rest.representation;
import static com.google.code.hibernate.rest.internal.InternalPreconditions.checkNotNull;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import com.thoughtworks.xstream.MarshallingStrategy;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.collections.CollectionConverter;
import com.thoughtworks.xstream.converters.collections.MapConverter;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;

/**
 * 
 * @author wangzijian
 * 
 */
public class XStreamBuilder {

	private HierarchicalStreamDriver driver;
	private MarshallingStrategy strategy;
	private Map<String, Class<?>> alias = new HashMap<String, Class<?>>();
	
	public XStream build() {
		checkNotNull(driver, "driver");
		XStream xStream = new XStream(driver);
		aliasEntityName(xStream);
		enhanceCollectionConverter(xStream);
		enhanceMapConverter(xStream);
		xStream.autodetectAnnotations(true);
		xStream.aliasSystemAttribute(null, "class");
		xStream.alias("Entities", CategoryList.class);
		xStream.setMarshallingStrategy(strategy);
		return xStream;
	}

	public XStreamBuilder driver(HierarchicalStreamDriver driver) {
		this.driver = checkNotNull(driver, "driver");
		return this;
	}
	
	public XStreamBuilder strategy(MarshallingStrategy strategy) {
		this.strategy = checkNotNull(strategy, "strategy");
		return this;
	}
	
	public XStreamBuilder alias(Map<String, Class<?>> alias) {
		this.alias = checkNotNull(alias, "alias");
		return this;
	}
	
	private void aliasEntityName(XStream xStream) {
		for (Entry<String, Class<?>> each : alias.entrySet()) {
			xStream.alias(each.getKey(), each.getValue());
		}
	}

	private void enhanceMapConverter(XStream xStream) {
		xStream.registerConverter(new MapConverter(xStream.getMapper()) {
			@Override
			@SuppressWarnings("rawtypes")
			public boolean canConvert(Class type) {
				return Map.class.isAssignableFrom(type);
			}
		});
	}

	private void enhanceCollectionConverter(XStream xStream) {
		xStream.registerConverter(new CollectionConverter(xStream.getMapper()) {
			@Override
			@SuppressWarnings("rawtypes")
			public boolean canConvert(Class type) {
				return Collection.class.isAssignableFrom(type);
			}
		});
	}
}
